import random
import re
from datetime import datetime

from flask import current_app, jsonify
from flask import make_response
from flask import request
from flask import session

from . import register_blue
from info.utils.captcha.captcha import captcha
from info import sr_redis, db
from info import constants
from info.utils.response_code import RET
from info.library.yuntongxun import sms
from info.models import User


# 定义路由用来获取从前端发送过来的图片请求以及设置好的图片编号
@register_blue.route('/image_code')
def register():
    # 从请求内容中获取图片验证码的编号
    image_id = request.args.get('code_id')
    # 判断图片验证码是否存在
    if not image_id:
        return jsonify(error=RET.PARAMERR, error_info='参数不存在')
    # 利用导入的模块生成新的图片验证码 获得图片验证码的名称，内容以及图片
    name, text, image = captcha.generate_captcha()
    # 将获取到的图片验证码的内容存入到redis数据库中,并设置过期时间,因为不确定是否会发生异常，将其放在try方法中
    try:
        sr_redis.setex('image_id:' + image_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
        # 出现异常就返回错误信息
    except Exception as e:
        current_app.logger.error(e)
        return make_response(jsonify(error=RET.OK, error_info='保存图片验证码失败'))
        # 没有异常则返回客户端请求的图片，并设置返回的图片的类型
    else:
        response = make_response(image)
        response.headers['Content-Type'] = 'image/jgp'
        return response


@register_blue.route('/send_message', methods=['POST'])
def send_message():
    # 首先确定手机号，图片验证码以及图片验证码的编号是否存在，并验证其正确性
    # 不存在则需要用户重新获取图片验证码以及重新输入正确的手机号
    # 存在则删除redis数据库中的数据，并将取出的数据与前段传入的图片验证码的数据进行一致性检查
    # 全部验证通过后，点击事件触发，发送验证短信，服务器也要存储短信验证码在redis数据库中
    # 取得用户输入的验证码，取出redis中的短信验证码，数据一致性检查
    # 取得前端发送回来的手机号，图片验证码的内容以及图片验证码的编号
    mobile = request.json.get('mobile')
    image_text = request.json.get('image_text')
    image_code_id = request.json.get('image_code_id')
    # 判断手机号，图片验证码的内容以及图片验证码的编号是否存在
    if not all([mobile, image_text, image_code_id]):
        return jsonify(errno=RET.PARAMERR, error_info='缺失参数')
    # 判断手机号的格式是否正确
    if not re.match(r'^1[3-9]\d{9}$', mobile):
        return jsonify(errno=RET.NODATA, error_info='手机号格式错误')
    # 判断图片验证码的编号是否正确，从redis数据库中取出数据对比其一致性,可能会出现异常，放进异常捕获中
    try:
        result_image_text = sr_redis.get('image_id:' + image_code_id)

    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, error_info="获取图片验证码失败")
        # 如果没有取到redis数据库中的数据，则表示其数据已经过期，也需要重新获取一次图片验证码
    if not result_image_text:
        return jsonify(errno=RET.NODATA, error_info='图片验证码已过期')
        # 如果取得到数据，则删除redis数据库中的内容，用户只允许输入一次图片验证码，输入错误的时候需要重新获取一个图片验证码，并再次将图片验证码的内容存入redis数据库中
    try:
        sr_redis.delete('image_id:' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        # 将取出的redis数据库中的内容与前段传过来的数据进行一致性检查
    if result_image_text.lower() != image_text.lower():
        return jsonify(errno=RET.DATAERR, error_info='验证码输入错误')

    # 判断该手机号是否注册
    # 先从数据库中取得满足该手机号的数据，判断能否取得，没有，则可以继续下一步发送短信，有则直接结束
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(erron=RET.DBERR, error_info='数据库查询异常')
    else:
        if user:
            return jsonify(erron=RET.DATAEXIST, error_info='该手机号已注册')

    # 全部验证完成则开始短信验证，先生成一个短信随机码，将其存入redis数据库需要进行唯一性标识，然后通过第三方发送给用户输入的手机号，从前端取得用户输入的短信验证码与redis数据库中的数据进行一致性检查
    # 生成一个随机的短信验证码
    sms_register = '%06d' % random.randint(0, 999999)
    # 进行唯一性标识后存入redis数据库中，设置过期时间，放入异常捕获中
    try:
        sr_redis.setex('SMS_id' + mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_register)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, error_info='存入数据库失败')
    # 将其通过第三方软件传给用户，可能会出现异常
    try:
        cpp = sms.CCP()
        sms_info = cpp.send_template_sms(mobile, [sms_register, constants.SMS_CODE_REDIS_EXPIRES / 60], "1")
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, error_info='发送短信异常')
    else:
        # 得到响应的信息，判断是否发送成功
        if sms_info == 0:
            return jsonify(errno=RET.OK, error_info='发送成功')
        else:
            return jsonify(errno=RET.THIRDERR, error_info='发送失败')


# 定义一个验证用户注册的路由
@register_blue.route('/info', methods=['POST'])
def register_info():
    # 获取用户的手机号，短信验证码以及密码并确定发其完整性
    # 判断手机号格式是否正确，并在数据库中查找该手机号，确定是第一次注册
    # 判断短信验证码是否正确
    # 将用户输入的密码加密后存入数据库

    # 获取手机号，短信验证码，以及密码
    mobile = request.json.get('mobile')
    sms_info = request.json.get('smscode')
    password = request.json.get('password')
    print(mobile, sms_info, password)
    print(type(sms_info))
    # 验证数据的完整性
    if not all([mobile, sms_info, password]):
        return jsonify(erron=RET.PARAMERR, error_info='参数缺失')
    # 验证手机号的格式是否正确
    if not re.match(r'1[3-9]\d{9}$', mobile):
        return jsonify(erron=RET.DATAERR, error_info='手机号格式错误')
    # 判断该手机号是否已经注册
    # 从数据库先取得满足该手机号的用户信息，放入异常捕获中
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(erron=RET.DBERR, error_info='查询数据失败')
    else:
        if user:
            return jsonify(erron=RET.DATAEXIST, error_info='该手机号已注册')
    # 判断短信验证码是否正确，先从redis数据库中取得短信验证码，与前段传来的短信验证码比较，验证其一致性
    # 获取之前存在redis数据库中的短信验证码的内容，放入异常捕获中
    try:
        result_sms_info = sr_redis.get('SMS_id' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(erron=RET.DBERR, error_info='获取数据失败')
    # 判断能否取得短信验证码
    if not result_sms_info:
        return jsonify(erron=RET.NODATA, error_info='短信验证码已过期')
    # 判断短信验证码与前段获取的是否一致,必须将获取的短信验证码转成字符串
    if result_sms_info != sms_info:
        # if result_sms_info != str(sms_info):
        return jsonify(erron=RET.DATAERR, error_info='短信验证码错误')
    # 对比完成后需要删除redis数据库中的短信验证码，放入异常捕获中
    try:
        sr_redis.delete('SMS_id' + mobile)
    except Exception as e:
        current_app.logger.error(e)
    # 对用户输入的密码进行加密,并将所有用户的信息存入mysql数据库中的User表中
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    user.password = password
    # 用户的信息存入mysql数据库中，放入异常捕获中
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 发生异常的话需要重新提交数据
        db.session.rollback()
        return jsonify(erron=RET.DATAERR, error_info='数据提交失败')
    else:
        # 存入mysql数据库成功后，将信息缓存到redis数据库中，后面用户登陆后需要用到
        session['user_id'] = user.id
        session['user_mobile'] = user.mobile
        session['user_nick_name'] = mobile
        return jsonify(erron=RET.OK, error_info='数据存入成功')


# 定义一个登陆路由
@register_blue.route('/landing', methods=['POST'])
def landing():
    # 获取用户登陆的手机号和密码
    # 确保数据的完整性，判断手机号的格式是否正确
    mobile = request.json.get('mobile')
    password = request.json.get('password')
    print(mobile, password)
    if not all([mobile, password]):
        return jsonify(erron=RET.NODATA, error_info='参数不完整')
    # 判断手机号格式是否正确
    if not re.match(r'1[3-9]\d{9}$', mobile):
        return jsonify(erron=RET.DATAERR, error_info='手机号格式错误')
    # 确认用户的数据是否存在数据库中，放入异常捕获中
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(erron=RET.NODATA, error_info='查询数据失败')
    # if not user:
    #     return jsonify(error=RET.NODATA,error_info='用户名错误')
    # if not user.check_password(password):
    #     return jsonify(erron=RET.NODATA,error_info='密码错误')
    # 保证数据的安全性
    if not user or not user.check_password(password):
        return jsonify(erron=RET.NODATA, error_info='用户名或密码错误')
    # 保存用户的登陆时间
    user.last_login = datetime.now()
    # 提交用户信息，放入异常捕获
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        # 存入数据库失败需要回滚操作，返回到未提交之前
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(erron=RET.DBERR, error_info='数据写入失败')
    else:
        session['user_id'] = user.id
        session['user_mobile'] = user.mobile
        # 因为用户可能登陆之后修改了昵称，所以需要存入用户修改之后的昵称
        session['user_nick_name'] = user.nick_name
        return jsonify(erron=RET.OK, error_info="数据保存成功")


# 定义一个退出路由
@register_blue.route('/outlanding')
def outlanding():
    # 退出后清除redis数据库中缓存，清除状态保持
    session.clear()
    # 清除redis数据库中的缓存，但是下一个用户访问也需要缓存到redis数据库中，不能清空，只能删除当前用户的值，并重新赋值为空
    session.pop('user_id', None)
    session.pop('user_mobile', None)
    session.pop('user_nick_name', None)
    return jsonify(erron=RET.OK, error_info="退出成功")